/** @file    MediaDirectorImpl.cpp
 *  @author  Alessandro Polo
 *  @version $Id: MediaDirectorImpl.cpp 2821 2010-08-01 11:44:26Z alex $
 *  @brief
 * File containing methods for the MediaDirectorImpl class.
 * The header for this class can be found in MediaDirectorImpl.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include "MediaDirectorImpl.h"

 #include <core/Message.h>
 #include <core/Request.h>
 #include <core/Response.h>
 #include <core/MethodsCommon.h>
 #include <core/Bundle.h>
 #include <core/UserManager.h>

 #include <stdlib.h>
 #include <time.h>
 #include <algorithm>

using namespace std;
using namespace wosh;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

MediaDirectorImpl::MediaDirectorImpl( BundleGeneric& bundle, BusConnector& busDevices ) : BundleGenericWorker(bundle), BusDevices(busDevices)
 {
	this->listener = NULL;
	this->timeoutEval = 0;

	this->zoneRanking = 0;

 }

MediaDirectorImpl::~MediaDirectorImpl() {
	if ( isThreadRunning() ) {
		quitThread(10000);
	 }

 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////// RUN THREAD

void MediaDirectorImpl::runThread()
 {
	Log(LOG_VERBOSE, ":runThread(): STARTING" );

	while( this->running )
	 {
		if ( this->msgQueue.isEmpty() ) {
			ThreadImpl::sleepForMSec(100);
			setThreadAlive();
			continue;
		 }
		ThreadImpl::sleepForMSec(50);
		setThreadAlive();

		// get and remove it from queue
		Message* message = this->msgQueue.pop();
		if ( message == NULL ) continue;

		evalMessage(message);

		// finally delete the instance
		delete message;
	 }

	Log(LOG_VERBOSE, ":runThread(): EXITING" );
 }

////////////////////////////////////////////////////////////////////////////////////////////////// RUN THREAD
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void MediaDirectorImpl::evalMessageIncoming( const Message& message )
 {
	if ( message.isEmpty() ) return;
//	if ( message.getContent()->isResponse() || message.getContent()->isFact() )
//		msgQueue.push( message.clone() );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void MediaDirectorImpl::evalMessage( Message* message )
 {
	if ( message == NULL || message->isEmpty() ) return;

	this->zones.mutex().lockForWrite();
	MediaZone* zone = getZoneByDeviceUri_( message->getSource() );
	if ( zone != NULL ) {
		evalDeviceMessage_( zone, *message );
		this->zones.mutex().unLock();
		return;
	 }
	this->zones.mutex().unLock();

	if ( message->getContent()->isFact() ) {
		const Fact* fact = message->getContent()->asFact();

		if ( fact->getEvent() == _Device_EVENT_StateChanged ) {
			int value = -1;
			if ( fact->hasData() && fact->getData()->isKindOf<Variant>() )
				value = fact->getData()->as<Variant>()->toInteger();
			Log(LOG_INFO, ":evalMessage(): Evaluating StateChanged[%d] of %s..", value, message->getSource().toString().c_str() );
			this->zones.mutex().lockForWrite();
			std::map<string, MediaZone*>::iterator it_c;
			std::map<string, MediaZone*>::iterator it_c_end = this->zones.stdEnd();
			for ( it_c=this->zones.stdBegin(); it_c!=it_c_end; it_c++ ) {
				if ( it_c->second == NULL ) continue;
				if ( !it_c->second->getKeyMappings().existsKey_(message->getSource()) ) continue;
				int command = it_c->second->getKeyMappings().key_(message->getSource());
				execCommand(it_c->second, (MediaZone::PLAYER_COMMAND)command, value);
			 }
			this->zones.mutex().unLock();

		 }
		
	 }


 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void MediaDirectorImpl::evalDeviceMessage_( MediaZone* zone, const Message& message )
 {
	if ( zone == NULL || message.isEmpty() ) return;
	zone->setSeen();

	if ( message.getContent()->isResponse() ) {
		const Response* response = message.getContent()->asResponse();
		if ( response->getMethod() == _Player_METHOD_get_medialist ) {
			if ( !response->getData()->isKindOf<MediaList>() ) return;
			const MediaList* medialist = dynamic_cast<const MediaList*>(response->getData());
			Log(LOG_INFO, ":evalDeviceMessage_(%s): Updating MediaList ..", zone->getName().c_str() );
			updatedMediaListByDevice_(medialist, zone);
		 }
	 }
	else if ( message.getContent()->isFact() ) {
		const Fact* fact = message.getContent()->asFact();
		// event from a MULTIMEDIA DEVICE!
		if ( fact->getEvent() == _Player_EVENT_PlayStateChanged ) {
			Log(LOG_INFO, ":evalDeviceMessage_(%s): PlayStateChanged ..", zone->getName().c_str() );
		 }
		else if ( fact->getEvent() == _Player_EVENT_VolumeChanged ) {
			int value = -1;
			if ( fact->hasData() && fact->getData()->isKindOf<Variant>() )
				value = fact->getData()->as<Variant>()->toInteger();
			zone->setVolume( value );
			//zone->setMuted( bool value )
			Log(LOG_INFO, ":evalDeviceMessage_(%s): VolumeChanged [%d]", zone->getName().c_str(), value );
		 }
		else if ( fact->getEvent() == _Player_EVENT_PlayListChanged ) {
			Log(LOG_INFO, ":evalDeviceMessage_(%s): PlayListChanged, Requesting..", zone->getName().c_str() );
			controlDevice( new Request(_METHOD_List), message.getSource() );
		 }
		else if ( fact->getEvent() == _Player_EVENT_PlayItemChanged ) {
			string trackPath = "";
			if ( fact->hasData() && fact->getData()->isKindOf<Variant>() )
				trackPath = fact->getData()->as<Variant>()->toString();
			Log(LOG_INFO, ":evalDeviceMessage_(%s): PlayItemChanged [%s]", zone->getName().c_str(), trackPath.c_str() );
			zone->getPlayList().setCurrentTrack( trackPath );
		 }
	 }


 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

/*
 use cases:
  - load a playlist to a zone (nomux);
	=> deep-copy MediaList in zone
	=> update (smart) MediaList's role of the target zone

  - synch from player device
	=> retrieve current MediaList



  - move a playlist across zones
	- load a playlist to target zone
	- synch against player device (of target zone)
	- update source zone state
	- synch against player device (of source zone)




*/


//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// load a playlist to a zone (mux)
WRESULT MediaDirectorImpl::copyMediaListToZone( const string& playlist, const string& targetZone, const string& sourceZone )
 {
	this->playlists.mutex().lockForRead();
	MediaList* plist = this->playlists.key_(playlist);
	if ( plist == NULL ) {
		this->playlists.mutex().unLock();
		Log(LOG_WARNING, ":copyMediaListToZone(%s,%s) Playlist not found!", playlist.c_str(), targetZone.c_str() );
		return WRET_ERR_PARAM;
	 }
	this->zones.mutex().lockForRead();
	MediaZone* target_zone = this->zones.key_(targetZone);
	if ( target_zone == NULL ) {
		this->playlists.mutex().unLock();
		this->zones.mutex().unLock();
		Log(LOG_WARNING, ":copyMediaListToZone(%s,%s) Zone not found!", playlist.c_str(), targetZone.c_str() );
		return WRET_ERR_PARAM;
	 }
	MediaZone* source_zone = this->zones.key_(sourceZone);
	WRESULT ret = copyMediaListToZone_(plist, target_zone, source_zone);
	this->playlists.mutex().unLock();
	this->zones.mutex().unLock();
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// load a playlist to a zone (nomux)
WRESULT MediaDirectorImpl::copyMediaListToZone_( const MediaList* list, MediaZone* targetZone, MediaZone* sourceZone )
 {
	if ( list == NULL || targetZone == NULL ) return WRET_ERR_PARAM;

	Log(LOG_INFO, ":copyMediaListToZone(%s,%s) : Overwriting MediaList..", list->getName().c_str(), targetZone->getName().c_str() );
	targetZone->getPlayList() = *list;

	// update (smart) MediaList's role of the target zone
	MediaZone* alsoPlaying = NULL;
	std::map<string, MediaZone*>::const_iterator it_z;
	for ( it_z=this->zones.stdBegin(); it_z!=this->zones.stdEnd(); it_z++ ) {
		if ( it_z->second == NULL ) continue;
		if ( it_z->second->getPlayList().getName() != list->getName() ) continue;
		if ( alsoPlaying != NULL && it_z->second->isRoleMaster() && !alsoPlaying->isRoleMaster() )
			alsoPlaying = it_z->second;
		alsoPlaying = it_z->second;
	 }
	if ( alsoPlaying != NULL ) {
		if ( alsoPlaying->isRoleMaster() )
			targetZone->setZoneRole(MediaZone::ROLE_SLAVE);
		else
			targetZone->setZoneRole(MediaZone::ROLE_FORK);
	}
	else if ( sourceZone != NULL && sourceZone->isRoleMaster() )
		targetZone->setZoneRole(MediaZone::ROLE_SLAVE);
	else {
		Log(LOG_INFO, ":copyMediaListToZone(%s) : %s Elected as Master..", list->getName().c_str(), targetZone->getName().c_str() );
		targetZone->setZoneRole(MediaZone::ROLE_MASTER);
	 }

	// default is slave
	if ( targetZone->getZoneRole() == MediaZone::ROLE_UNKNOWN )
		targetZone->setZoneRole(MediaZone::ROLE_SLAVE);
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// synch against player device (mux)

WRESULT MediaDirectorImpl::synchZoneAgainstPlayer( const string& playlist, const string& targetZone )
 {
	this->playlists.mutex().lockForRead();
	MediaList* plist = this->playlists.key_(playlist);
	if ( plist == NULL ) {
		this->playlists.mutex().unLock();
		Log(LOG_WARNING, ":synchZoneAgainstPlayer(%s,%s) Playlist not found!", playlist.c_str(), targetZone.c_str() );
		return WRET_ERR_PARAM;
	 }
	this->zones.mutex().lockForRead();
	MediaZone* target_zone = this->zones.key_(targetZone);
	if ( target_zone == NULL ) {
		this->playlists.mutex().unLock();
		this->zones.mutex().unLock();
		Log(LOG_WARNING, ":synchZoneAgainstPlayer(%s,%s) Zone not found!", playlist.c_str(), targetZone.c_str() );
		return WRET_ERR_PARAM;
	 }
	WRESULT ret = synchZoneAgainstPlayer(plist, target_zone);
	this->playlists.mutex().unLock();
	this->zones.mutex().unLock();
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// synch against player device
WRESULT MediaDirectorImpl::synchZoneAgainstPlayer( const MediaList* playlist, MediaZone* zone )
 {
	if ( zone == NULL || playlist == NULL ) return WRET_ERR_PARAM;
	if ( !zone->getPlayerURI().isValid() ) return WRET_ERR_ILLEGAL_USE;
	// send a synch command with the MediaList object
	controlDevice( new Request(_Player_METHOD_synch,playlist->clone()), zone->getPlayerURI() );
	zone->getPlayList() = *playlist;
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// send request to synch from player device
WRESULT MediaDirectorImpl::synchZoneFromPlayerRequest( MediaZone* zone )
 {
	if ( zone == NULL ) return WRET_ERR_PARAM;
	if ( !zone->getPlayerURI().isValid() ) return WRET_ERR_ILLEGAL_USE;
	// send a synch command with the MediaList object
	controlDevice( new Request(_Player_METHOD_get_medialist), zone->getPlayerURI() );
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// update medialist of a zone BY its player (synch from player device)
WRESULT MediaDirectorImpl::updatedMediaListByDevice_( const MediaList* playlist, MediaZone* zone )
 {
	if ( zone == NULL || playlist == NULL ) return WRET_ERR_PARAM;
	// deep-copy MediaList in zone
	zone->getPlayList() = *playlist;

	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// send a generic message to a Device
void MediaDirectorImpl::controlDevice( MessageContent* content, const URI& device_uri )
 {
	Message* message = new Message( content );
	message->setSource(this->getOwner());
	message->setDestination(device_uri);
	message->setDestinationBus( _Bus_Devices );
	BusDevices.postMessage(message);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////











void MediaDirectorImpl::editZoneRanking_( const string& zonename, bool more_or_less )
 {
	std::map<string, MediaZone*>::const_iterator it_c;
	for ( it_c=this->zones.stdBegin(); it_c!=this->zones.stdEnd(); it_c++ ) {
		if ( it_c->second == NULL ) continue;
		double total_hits = it_c->second->getRanking() * this->zoneRanking;
		if ( it_c->second->getName() == zonename  ) {
			if ( more_or_less)
				++total_hits;
			else
				--total_hits;
		 }
		it_c->second->setRanking( (this->zoneRanking+1) / total_hits );
	 }
	++this->zoneRanking;
 }





//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

MediaZone* MediaDirectorImpl::getZoneByDeviceUri_( const URI& device_uri )
 {
	std::map<string, MediaZone*>::const_iterator it_c;
	for ( it_c=this->zones.stdBegin(); it_c!=this->zones.stdEnd(); it_c++ ) {
		if ( it_c->second == NULL ) continue;
		if ( it_c->second->getPlayerURI() != device_uri ) continue;
		return it_c->second;
	 }
	return NULL;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

MediaZone* MediaDirectorImpl::getPlayerOfLocation_( const string& locationUri )
 {
	std::map<string, MediaZone*>::const_iterator it_c;
	for ( it_c=this->zones.stdBegin(); it_c!=this->zones.stdEnd(); it_c++ ) {
		if ( it_c->second == NULL ) continue;
		if ( it_c->second->getLocationURI() != locationUri ) continue;
		return it_c->second;
	 }
	return NULL;
 }
/*
MediaZone* MediaDirectorImpl::getPlayerByBuildingObj_( const string& bobjUri )
 {
	std::map<string, MediaZone*>::const_iterator it_c;
	for ( it_c=this->zones.stdBegin(); it_c!=this->zones.stdEnd(); it_c++ ) {
		if ( it_c->second == NULL ) continue;
//		if ( it_c->second->getBuildingObjectURI() != bobjUri ) continue;
//		return it_c->second;
	 }
	return NULL;
 }
*/
MediaZone* MediaDirectorImpl::getNearestMediaZone_( MediaZone* zone, bool playing_flag )
 {
	MediaZone* targetZone = NULL;
	std::map<string, MediaZone*>::const_iterator it_c;
	for ( it_c=this->zones.stdBegin(); it_c!=this->zones.stdEnd(); it_c++ ) {
		if ( it_c->second == NULL ) continue;
		if ( it_c->second == zone ) continue;
		if ( playing_flag && !it_c->second->isPlaying() ) continue;

		targetZone = it_c->second;
	 }
	return targetZone;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT MediaDirectorImpl::execCommand( MediaZone* player, MediaZone::PLAYER_COMMAND command, int command_value )
 {
	if ( player == NULL ) {
		Log(LOG_CRITICAL, ":execCommand() FAILED : Zone NOT found!" );
		return WRET_ERR_PARAM;
	 }

	Request* request = NULL;
	switch( command ) {
		case MediaZone::COMMAND_PLAY: {

				if ( player->isPlaying() ) {
				 }
				else {
				 }

				if ( command_value >= 100 ) {
					bool move = false;
					MediaZone* source = getNearestMediaZone_( player, true );
					if ( source == NULL ) {
						return restoreZone_( player );
					 }
					else {
						return synchZones_( source, player, move );
					 }
				 }
				else if ( command_value <= 0 ) {
					request = new Request( _Player_METHOD_stop );
				 }
				break;
			 }
		case MediaZone::COMMAND_TRACK: {

				if ( player->isPlaying() ) {
				 }

				if ( command_value == 100 ) { // ON was pressed
					request = new Request( _Player_METHOD_prev );
				 }
				else if ( command_value == 0 ) { // OFF was pressed
					request = new Request( _Player_METHOD_next );
				 }
				else { // DIM/BRIGHT was pressed
					// we consider EACH press as an uniform step, so we don't care of real state value
					if ( command_value < 0 ) // DIM was pressed [=> -10% by default]
						request = new Request( _Player_METHOD_set_position, "-" );
					else // BRIGHT was pressed [=> +10% by default]
						request = new Request( _Player_METHOD_set_position, "+" );
				 }
				break;
			 }
		case MediaZone::COMMAND_VOLUME: {
				if ( command_value == 100 ) { // ON was pressed
					request = new Request( _Player_METHOD_set_mute, Data(false) );
				 }
				else if ( command_value == 0 ) { // OFF was pressed
					request = new Request( _Player_METHOD_set_mute, Data(true) );
				 }
				else { // DIM/BRIGHT was pressed
					// we consider EACH press as an uniform step, so we don't care of real state value
					if ( command_value < 0 ) // DIM was pressed [=> -2 by default]
						request = new Request( _Player_METHOD_set_volume, "-" );
					else // BRIGHT was pressed [=> +2 by default]
						request = new Request( _Player_METHOD_set_volume, "+" );
				 }
				break;
			 }
		case MediaZone::COMMAND_UNKNOWN:
		default: {
				break;
			 }
	 }

	if ( request == NULL ) {
		return WRET_ERR_PARAM;
	 }
	if ( this->listener == NULL ) {
		Log(LOG_CRITICAL, ":execCommand() FAILED : NULL Listener" );
		return WRET_ERR_INTERNAL;
	 }
	Log(LOG_INFO, ":execCommand(%d) Sending '%s' to %s..", (int)command, request->getMethod().c_str(), player->getURI().getName().c_str() );

	Message* message = new Message( request );
	message->setSource(this->getOwner());
	message->setDestination(player->getURI());
	message->setDestinationBus(_Bus_Devices);
	BusDevices.postMessage(message);
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT MediaDirectorImpl::synchZones( const string& from_zone, const string& to_zone, bool move )
 {
	MutexLockerRead mL(&this->zones.mutex());
	MediaZone* playerSrc = getPlayerOfLocation_( from_zone );
	MediaZone* playerDest = getPlayerOfLocation_( to_zone );
	if ( playerSrc == NULL ) {
		Log(LOG_CRITICAL, ":synchZones_(%s,%s) FAILED : Source Zone NOT found!", from_zone.c_str(), to_zone.c_str() );
		return WRET_ERR_PARAM;
	 }
	if ( playerDest == NULL ) {
		Log(LOG_CRITICAL, ":synchZones_(%s,%s) FAILED : Destination Zone NOT found!", from_zone.c_str(), to_zone.c_str() );
		return WRET_ERR_PARAM;
	 }
	return synchZones_( playerSrc, playerDest, move );
 }

WRESULT MediaDirectorImpl::synchZones_( MediaZone* playerSrc, MediaZone* playerDest, bool move )
 {
	if ( playerSrc == NULL || playerDest == NULL ) {
		Log(LOG_CRITICAL, ":synchZones_() FAILED : Invalid Zone(s)!" );
		return WRET_ERR_PARAM;
	 }
	if ( this->listener == NULL ) {
		Log(LOG_CRITICAL, ":synchZones_() FAILED : NULL Listener" );
		return WRET_ERR_INTERNAL;
	 }
	Log(LOG_INFO, ":synchZones_(%s,%s,%d) : Sending "_Player_METHOD_synch"..[synch %s from %s]",
														playerSrc->getLocationURI().c_str(), playerDest->getLocationURI().c_str(),
														playerDest->getURI().toString().c_str(), playerSrc->getURI().toString().c_str(), (int)move );

	Request* request = NULL;
	if ( move )
		request = new Request( _Player_METHOD_synch, playerDest->getURI().toString() );
	else
		request = new Request( _Player_METHOD_synch, playerDest->getURI().toString() );

	Message* message = new Message( request );
	message->setSource(this->getOwner());
	message->setDestination(playerSrc->getURI());
	message->setDestinationBus(_Bus_Devices);
	BusDevices.postMessage(message);
	return WRET_OK;
 }

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT MediaDirectorImpl::restoreZone( const string& zone )
 {
	MutexLockerRead mL(&this->zones.mutex());
	MediaZone* player = getPlayerOfLocation_( zone );
	if ( player == NULL ) {
		Log(LOG_CRITICAL, ":restoreZone(%s) FAILED : Zone NOT found!", zone.c_str() );
		return WRET_ERR_PARAM;
	 }
	if ( this->listener == NULL ) {
		Log(LOG_CRITICAL, ":restoreZone(%s) FAILED : NULL Listener", zone.c_str() );
		return WRET_ERR_INTERNAL;
	 }
	return restoreZone_( player );
 }

WRESULT MediaDirectorImpl::restoreZone_( MediaZone* player )
 {
	if ( player == NULL ) {
		Log(LOG_CRITICAL, ":synchZones_() FAILED : Invalid Zone(s)!" );
		return WRET_ERR_PARAM;
	 }
	if ( this->listener == NULL ) {
		Log(LOG_CRITICAL, ":restoreZone_(%s) FAILED : NULL Listener", player->getLocationURI().c_str() );
		return WRET_ERR_INTERNAL;
	 }

////////////////////////////////////////////////////////////////////////
// GUESS/LOAD THE PLAYLIST/ITEM
/*

*/
////////////////////////////////////////////////////////////////////////
	vector<MediaTrack*> tracks;
	WRESULT ret = this->listener->load_MediaTrack( player, tracks, this );
	if ( WFAILED(ret) || tracks.empty() ) {
		return WRET_ERR_INTERNAL;
	 }

	Request* request = new Request( _Player_METHOD_play, tracks.at(0)->getTrack().getPath() );
	Message* message = new Message( request );
	message->setSource(this->getOwner());
	message->setDestination(player->getURI());
	message->setDestinationBus(_Bus_Devices);
	BusDevices.postMessage(message);
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
